﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Net.Sockets;
using DataEngine.ContractModel;
using BNF.HMI.DAL;
using System.Runtime.InteropServices;
using BNF.Utility.TimeAPI;

namespace DataEngine.Communication
{


    public class DEStream
    {
        #region PROTECTED MEMBERS

        protected CTAPI myTimeLib = new CTAPI();
        protected bool m_IsConnect = false;
        protected bool m_IsBusy = false;
        protected NetworkStream ns;
       
        #endregion
        #region PUBLIC MEMBERS

        public bool IsConnected { get { return m_IsConnect; }  }
        public bool IsBusy { get { return m_IsBusy; }  }
        public bool Connect(String ServerIP,int Port)
        {
            try
            {
                TcpClient tcpClient = new TcpClient(ServerIP, Port);
                ns = tcpClient.GetStream();

                m_IsConnect = tcpClient.Connected;
            }
            catch (Exception)
            {
                m_IsConnect = false;
            }


            return m_IsConnect;
        }
        public void DisConnect()
        {
            ns.Close();
        }
        #endregion
    }
    #region DATA ENGINE READER
    /// <summary>
    /// DataEnginer Reader
    /// Developer: HaiMai
    /// </summary>
    public class DEReader: DEStream
    {
        #region PRIVATE MEMBERS
        private DataTable DefineTable()
        {
            DataTable myTab = new DataTable();
            myTab.Columns.Add("TagID",Type.GetType("System.UInt32"));
            myTab.Columns.Add("TimeStamp",Type.GetType("System.String"));
            myTab.Columns.Add("TimeStampNum", Type.GetType("System.UInt64"));
            myTab.Columns.Add("Value",Type.GetType("System.Object"));
            myTab.Columns.Add("Quality", Type.GetType("System.Byte"));

            return myTab;
        }
        private DataTable DefineTrendTable(List<UInt32> TagIDList)
        {
            DataTable myTab = new DataTable();
            // Common columns
            myTab.Columns.Add("TimeStamp", Type.GetType("System.String"));
            myTab.Columns.Add("TimeStampNum", Type.GetType("System.UInt64"));

            // Generate columns
            foreach (var item in TagIDList)
            {
                myTab.Columns.Add(item.ToString(), Type.GetType("System.Object"));
                myTab.Columns.Add(item.ToString() + "_Quality", Type.GetType("System.Byte"));
            }
            return myTab;
        }
        private bool SendSync(ushort TrID, List<UInt32> TagIDList,CommandEnum requestType, REQUEST_HEADER requestHeader)
        {
            if (m_IsBusy) return false;

            m_IsBusy = true;
            PACKET_HEADER header = new PACKET_HEADER();
            header.Sign = 0x5A;
            header.Cmd = (byte)requestType;
            header.TrID = TrID;
            header.Length = (uint)(TagIDList.Count * 4 + ModelConstants.REQUEST_HEADER_SIZE);
            
            List<Object> myDataList = TagIDList.ConvertAll<Object>(delegate(UInt32 item){ return (Object) item; });
            int sentCount = clsSocket.Serialize(ns, header, requestHeader, myDataList);
            return true;
        }
        private DataTable ReceiveSync(ushort TrID)
        {
            DataTable myRet = DefineTable();
            byte[] InBuffer = new Byte[100000];
            int count = 0;
            // Read the header first
            ns.Read(InBuffer, 0, 8); count += 8;
            PACKET_HEADER myDataHeader = (PACKET_HEADER)clsSocket.Deserialize(InBuffer, typeof(PACKET_HEADER));
            if (myDataHeader.Length > 0 && myDataHeader.TrID ==TrID)
            {
                count += (int)myDataHeader.Length;
                clsSocket.ReadN(ns, InBuffer, 8, (int)myDataHeader.Length);
                DATA_POINT myData;
                int MyItemCount = Convert.ToInt32(myDataHeader.Length /ModelConstants.INSERT_DATA_SIZE);
                List<Object> myList = clsSocket.Deserialize(InBuffer, ModelConstants.PACKET_HEADER_SIZE, typeof(DATA_POINT), MyItemCount);

                // Show data=============
                foreach (Object item in myList)
                {
                    myData = (DATA_POINT)item;
                    DataRow myRow = myRet.NewRow();
                    myRow["TagID"]=myData.TagID;
                    myRow["TimeStamp"]= myTimeLib.JavaTimeToDateTime((long)myData.TimeStamp).ToString("yyyy/MM/dd HH:mm:ss.fff");
                    myRow["TimeStampNum"] = myData.TimeStamp;
                    myRow["Value"]=clsSocket.GetValueFromVariant(myData.Value,(VarEnum)Enum.ToObject(typeof(VarEnum),(int)myData.DataType));
                    myRow["TagID"]=myData.TagID;
                    myRow["Quality"] = (myData.TimeStamp!=0)?QualityEnum.QUALITY_GOOD:QualityEnum.QUALITY_BAD;

                    myRet.Rows.Add(myRow);

                }

            }
            else
            {
                Console.WriteLine("NO DATA");
            }

            Console.WriteLine(String.Format("====>Received {0} bytes [{1}] objects", count, (count - ModelConstants.PACKET_HEADER_SIZE) / ModelConstants.INSERT_DATA_SIZE));
            m_IsBusy = false;
            return myRet;
        }
        #endregion

        #region SYNC METHODS
        public bool Connect()
        {
            return Connect("127.0.0.1", 9101);
        }
        public bool Connect(String ServerIP)
        {
            return Connect(ServerIP, 9101);
        }
        public DataTable GetPointSync(ushort TrID,List<UInt32> TagIDList)
        {
            if (!SendSync(TrID, TagIDList, CommandEnum.REQUEST_POINT, new REQUEST_HEADER()))
            {
                return null;
            }
            else
            {
                return ReceiveSync(TrID);
            }
        }
        public DataTable GetPointSync(ushort TrID,List<UInt32> TagIDList,ulong timestamp)
        {
            REQUEST_HEADER myRequest = new REQUEST_HEADER();
            myRequest.endtime = timestamp;
            if (!SendSync(TrID, TagIDList, CommandEnum.REQUEST_POINT, myRequest))
            {
                return null;
            }
            else
            {
                return ReceiveSync(TrID);
            }
        }
        public DataTable GetListSync(ushort TrID, List<UInt32> TagIDList, ulong timestamp, uint duration)
        {
            REQUEST_HEADER myRequest = new REQUEST_HEADER();
            myRequest.endtime = timestamp;
            myRequest.duration = duration;
            if (!SendSync(TrID, TagIDList, CommandEnum.REQUEST_POINT, myRequest))
            {
                return null;
            }
            else
            {
                return ReceiveSync(TrID);
            }
        }
        public DataTable GetTrendSync(ushort TrID, List<UInt32> TagIDList, ulong timestamp, uint duration, uint resolution)
        {
            if (TagIDList.Count > 10 || resolution <=0) return null;

            REQUEST_HEADER myRequest = new REQUEST_HEADER();
            myRequest.endtime = timestamp;
            myRequest.duration = duration;
            myRequest.resolution = resolution;

            if (!SendSync(TrID, TagIDList, CommandEnum.REQUEST_POINT, myRequest))
            {
                return null;
            }
            else
            {
                DataTable myTab= ReceiveSync(TrID);
                if (myTab == null || myTab.Rows.Count == 0) return null;

                if (timestamp == 0) timestamp = (ulong)myTimeLib.GetCurrentTimeAsJavaTime();
                DataTable myRet = DefineTrendTable(TagIDList);
                //Time Processing
                ulong starttime = timestamp - duration;
                ulong mytime;
                uint count = duration / resolution;
                for (int i = 0; i < count; i++)
                {
                    DataRow myRetRow = myRet.NewRow();
                    mytime = starttime + (ulong)(i+1) * resolution;
                    myRetRow["TimeStamp"] = myTimeLib.JavaTimeToDateTime((long)mytime).ToString("yyyy/MM/dd HH:mm:ss.fff"); ;
                    myRetRow["TimeStampNum"] = mytime;
                    myRet.Rows.Add(myRetRow);
                }

                int TagDataCount = 0; //from 0=>Count
                foreach (DataRow myRow in myTab.Rows)
                {
                    if (TagDataCount == count ) { TagDataCount = 0; }
                    myRet.Rows[TagDataCount][myRow[0].ToString()] = myRow[3]; //Value
                    myRet.Rows[TagDataCount][myRow[0].ToString()+"_Quality"] = myRow[4]; //Quality
                    TagDataCount++;
                }
                return myRet;
            }
        }
        public DataTable GetDataSync(ushort TrID, List<UInt32> TagIDList, ulong timestamp, uint duration, uint resolution)
        {
            REQUEST_HEADER myRequest = new REQUEST_HEADER();
            myRequest.endtime = timestamp;
            myRequest.duration = duration;
            myRequest.resolution = resolution;
            if (!SendSync(TrID, TagIDList, CommandEnum.REQUEST_POINT, myRequest))
            {
                return null;
            }
            else
            {
                return ReceiveSync(TrID);
            }
        }
        #endregion

        #region ASYNC METHODS

        public event ASyncHandler OnValueChanged;
        public delegate void ASyncHandler(Object sender, DECommEventArgs e);

        public void ConnectASync(String ServerIP)
        {
            throw new NotImplementedException();
        }
        public bool GetPointASync()
        {
            throw new NotImplementedException();
        }
        public bool GetPointASync(ulong timestamp)
        {
            throw new NotImplementedException();
        }
        public bool GetListASync(ulong timestamp, uint duration)
        {
            throw new NotImplementedException();
        }
        public bool GetTrendASync(ulong timestamp, uint duration, uint resolution)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
    #endregion
    #region DATA ENGINE WRITER

    /// <summary>
    /// DataEnginer Writer
    /// Developer: HaiMai
    /// </summary>
    public class DEWriter:DEStream
    {
        #region SYNC METHODS
        public bool Connect()
        {
            return Connect("127.0.0.1", 9100);
        }
        public bool Connect(String ServerIP)
        {
            return Connect(ServerIP, 9100);
        }
        public bool InsertDataSync(ushort TrID,List<Object> DataList)
        {
            if (m_IsBusy) return false;

            m_IsBusy = true;
            PACKET_HEADER myHeader = new PACKET_HEADER();
            myHeader.Sign = 0x5A;
            myHeader.Cmd = (byte)CommandEnum.INSERT_DATA;
            myHeader.TrID = TrID;
            myHeader.Length = Convert.ToUInt32(DataList.Count * ModelConstants.INSERT_DATA_SIZE);
            
            clsSocket.Serialize(ns, myHeader, DataList);
            m_IsBusy = false;
            return true;
        }
        #endregion
    }
    #endregion

    public enum DataType
    {
        POINT,
        LIST,
        TREND
    }
    public class DECommEventArgs : EventArgs
    {
        private DataTable m_Data;
        private DataType m_Type;
        private String m_Message;

        public DECommEventArgs(DataTable p_Data, DataType p_Type, String p_Message)
        {
            this.m_Data = p_Data;
            this.m_Type = p_Type;
            this.m_Message = p_Message;
        }

        public DataTable Data { get { return m_Data; } }
        public DataType Type { get { return m_Type; } }
        public String Message { get { return m_Message; } }
    }
}
